﻿using System;
using System.Collections;
using System.Collections.Generic;

using zijian666.Converts.Abstractions;
using zijian666.Converts.Convertor.Base;
using zijian666.Converts.Core;
using zijian666.Converts.Extensions;

namespace zijian666.Converts.Convertor;

/// <summary>
/// <seealso cref=" IDictionary{TKey, TValue}"/> 字典转换器
/// </summary>
/// <typeparam name="TDictionary">实现了 <seealso cref="IDictionary{TKey, TValue}"/> 的类型</typeparam>
/// <typeparam name="TKey">字典键类型</typeparam>
/// <typeparam name="TValue">字典值类型</typeparam>
public class DictionaryConvertor<TDictionary, TKey, TValue> : AllowNullConvertor<TDictionary>, IFrom<object, TDictionary?>, IFrom<Dictionary<string, object>, TDictionary?>
    where TDictionary : class, IDictionary<TKey, TValue?>
{

    /// <summary>
    /// 转换器有错误
    /// </summary>
    private readonly bool _error;

    /// <summary>
    /// 构造函数
    /// </summary>
    public DictionaryConvertor() => _error = OutputType.MatchType(typeof(Dictionary<TKey, TValue>), typeof(IDictionary<TKey, TValue>)) is not MacthedType.Equals and not MacthedType.Acceptable and not MacthedType.TryCreate;

    /// <inheritdoc />
    public ConvertResult<TDictionary?> From(IConvertContext context, object input)
    {
        if (context is null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (input is null)
        {
            throw new ArgumentNullException(nameof(input));
        }

        if (_error)
        {
            return ExceptionHelper.InstantiationFail(TypeFriendlyName, context.Settings.CultureInfo);
        }

        var enumerator = new KeyValueEnumerator<TKey, TValue>(context, input);

        if (enumerator.NotEnumerator)
        {
            return context.ConvertFail(this, input);
        }

        var dict = OutputType.IsInterface
            ? (IDictionary<TKey, TValue?>)new Dictionary<TKey, TValue?>()
            : context.Settings.CreateInstance<TDictionary>();

        while (enumerator.MoveNext())
        {
            var key = enumerator.GetKey();
            if (!key.Success)
            {
                return ExceptionHelper.CollectionKeyFail(TypeFriendlyName, enumerator.OriginalKey + "", context.Settings.CultureInfo);
            }

            var value = enumerator.GetValue();
            if (!value.Success)
            {
                return ExceptionHelper.CollectionAddFail(TypeFriendlyName, key.Value + "", enumerator.OriginalValue, context.Settings.CultureInfo);
            }

            dict.Add(key.Value!, value.Value);
        }
        return (TDictionary)dict;
    }

    /// <inheritdoc />
    public ConvertResult<TDictionary?> From(IConvertContext context, Dictionary<string, object> input)
    {
        if (context is null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (input is null)
        {
            throw new ArgumentNullException(nameof(input));
        }

        return From(context, (object)input);
    }
}
